(function() {
    PRIVILEGE = {
        NONE: 0,
        DOM: 1,
        RING: 2
    };

    window.stop();

    function findRing(node) {
        var temp = node;
        while (true) {
            if (temp.nodeName === 'div' && temp.hasAttribute('ring')) {
                return temp.getAttribute('ring');
            } else {
                temp = temp.parentNode;
                if (!temp) {
                    return Infinity;
                }
            }
        }
    }

    function hasScript(node) {
        var attr = node.attributes,
            i = attr.length;
        if (node.nodeName === 'script') {
            return true;
        }
        while (i--) {
            if (attr[i].nodeName.indexOf('on') === 0) {
                return true;
            }
        }
        return false;
    }

    // Recursively walk down the syntax tree to Identifiers and Literals
    function getPrivilege(exp, elemId, elemCls, prvlg, ring) {
        var i, ilength,
            privilege1 = PRIVILEGE.NONE,
            privilege2 = PRIVILEGE.NONE,
            elements;
        // console.log(exp);
        switch (exp.type) {
            case 'CallExpression':
                // console.log('call expression', exp.callee, exp.arguments);
                if (exp.callee.type !== 'Identifier') {
                    privilege1 = getPrivilege(exp.callee, elemId, elemCls, prvlg, ring);
                } else {
                    if (prvlg[exp.callee.name]) {
                        privilege1 = prvlg[exp.callee.name];
                    }
                }
                for (i = 0, ilength = exp.arguments.length; i < ilength; i++) {
                    if (exp.arguments[i].type !== 'Identifier' &&
                        exp.arguments[i].type !== 'Literal') {
                        privilege2 = getPrivilege(exp.arguments[i], elemId, elemCls, prvlg, ring);
                        if (privilege2 === PRIVILEGE.RING) {
                            return PRIVILEGE.RING;
                        }
                    } else if (exp.arguments[i].type === 'Identifier') {
                        // console.log(exp.arguments[i]);
                        if (prvlg[exp.arguments[i].name] === PRIVILEGE.RING) {
                            return PRIVILEGE.RING;   
                        }
                    }
                }
                if (privilege1 === PRIVILEGE.DOM) {
                    if (exp.callee.type !== 'Identifier') {
                        if (exp.callee.property.name === 'getElementById') {
                            if (elemId[exp.arguments[0].value].getAttribute('ring') < ring) {
                                return PRIVILEGE.RING;
                            }
                        } else if (exp.callee.property.name === 'getElementsByClassName') {
                            elements = elemCls[exp.arguments[0].value];
                            for (i = 0, ilength = elements.length; i < ilength; i++) {
                                if (elements[i].getAttribute('ring') < ring) {
                                    return PRIVILEGE.RING;
                                }
                            }
                        }
                    } else {
                        // TODO case var d = document.getElementById;
                    }
                }
                return privilege1;
            case 'MemberExpression':
                // console.log('member expression', exp.object, exp.property);
                if (exp.object.type !== 'Identifier' &&
                    exp.object.type !== 'Literal') {
                    privilege1 = getPrivilege(exp.object, elemId, elemCls, prvlg, ring);
                } else if (exp.object.type === 'Identifier') {
                    if (exp.object.name === 'window' &&
                        exp.property.name === 'document') { // window.document
                        privilege1 = PRIVILEGE.DOM;
                    } else if (exp.object.name === 'document') {
                        if (exp.property.name === 'cookie' && ring > 0) { // document.cookie
                            privilege1 = PRIVILEGE.RING;
                        } else if (exp.property.name.indexOf('getElement') === 0) { // document.getElement
                            privilege1 = PRIVILEGE.DOM;
                        } else { // all other function calls for document.
                            privilege1 = PRIVILEGE.RING;
                        }
                    } else if (exp.object.name === 'console') {
                        privilege1 = PRIVILEGE.NONE;
                    } else {
                        if (prvlg[exp.object.name]) { // 
                            privilege1 = prvlg[exp.object.name];
                        } else {
                            privilege1 = PRIVILEGE.RING;
                        }
                    }
                }
                return privilege1;
            case 'AssignmentExpression':
                // console.log('assignment expression', exp.left, exp.operator, exp.right);
                if (exp.right.type !== 'Identifier' &&
                    exp.right.type !== 'Literal') {
                    privilege1 = getPrivilege(exp.right, elemId, elemCls, prvlg, ring);
                }
                if (!prvlg[exp.left.name]) {
                    prvlg[exp.left.name] = PRIVILEGE.NONE;
                }
                if (exp.right.type === 'Literal') { // foo = 'bar'
                    prvlg[exp.left.name] = PRIVILEGE.NONE;
                } else if (exp.right.type === 'Identifier') { // foo = bar
                    if (prvlg[exp.left.name] < prvlg[exp.right.name]) {
                        prvlg[exp.left.name] = prvlg[exp.right.name];
                    }
                } else { // foo = bar() or foo = bar + baz or foo = bar.baz
                    if (prvlg[exp.left.name] < privilege1) {
                        prvlg[exp.left.name] = privilege1;
                    }
                }
                return prvlg[exp.left.name];
            case 'BinaryExpression':
                // console.log('binary expression', exp.left, exp.operator, exp.right);
                if (exp.left.type !== 'Identifier' &&
                    exp.left.type !== 'Literal') {
                    privilege1 = getPrivilege(exp.left, elemId, elemCls, prvlg, ring);
                } else if (exp.left.type === 'Literal') {
                    privilege1 = PRIVILEGE.NONE;
                } else if (exp.left.type === 'Identifier') {
                    privilege1 = prvlg[exp.left.name];
                }
                if (exp.right.type !== 'Identifier' &&
                    exp.right.type !== 'Literal') {
                    privilege2 = getPrivilege(exp.right, elemId, elemCls, prvlg, ring);
                } else if (exp.right.type === 'Literal') {
                    privilege2 = PRIVILEGE.NONE;
                } else if (exp.right.type === 'Identifier') {
                    privilege2 = prvlg[exp.right.name];
                }
                return privilege1 > privilege2 ? privilege1 : privilege2;
            case 'Literal':
                return privilege1;
            default:
                console.log('Uncaught expression type:', exp.type);
        }
        return PRIVILEGE.NONE;
    }

    function checkRingPrivilege(body, elementsId, elementsClass, syntax, ring) {
        // console.log(JSON.stringify(syntax, null, 4));
        var i, j,
            ilength, jlength,
            privilege = {};
        // console.log(syntax);
        for (i = 0, ilength = syntax.body.length; i < ilength; i++) {
            if (syntax.body[i].type === 'VariableDeclaration') {
                for (j = 0, jlength = syntax.body[i].declarations.length; j < jlength; j++) {
                    if (syntax.body[i].declarations[j].init) {
                        privilege[syntax.body[i].declarations[j].id.name] =
                            getPrivilege(syntax.body[i].declarations[j].init,
                            elementsId, elementsClass, privilege, ring);
                    }
                    if (privilege[syntax.body[i].declarations[j].id.name] === PRIVILEGE.RING) {
                        delete syntax.body[i].declarations[j];
                    }
                }
            } else if (syntax.body[i].type === 'ExpressionStatement') {
                if (getPrivilege(syntax.body[i].expression, elementsId,
                    elementsClass, privilege, ring) === PRIVILEGE.RING) {
                    delete syntax.body[i];
                } 
            }
        }
        i = syntax.body.length;
        while (i--) {
            if (syntax.body[i] === undefined) {
                syntax.body.splice(i, 1);
            } else if (syntax.body[i].type === 'VariableDeclaration') {
                j = syntax.body[i].declarations.length;
                while (j--) {
                    if (syntax.body[i].declarations[j] === undefined) {
                        syntax.body[i].declarations.splice(j, 1);
                    }
                }
                if (syntax.body[i].declarations.length === 0) {
                    syntax.body.splice(i, 1);
                }
            }
        }
        // console.log(privilege);
        // console.log(to_be_removed);
        return escodegen.generate(syntax);
    }

    function processScript(body, elementsId, elementsClass, node) {
        // console.log(node);
        // TODO handle parsing error on the esprima
        var script,
            syntax,
            i = node.attributes.length,
            attr,
            ring = findRing(node);
        if (node.nodeName === 'script') {
            syntax = esprima.parse(node.textContent);
            node.textContent = checkRingPrivilege(body, elementsId,
                elementsClass, syntax, ring);
        } else {
            while (i--) {
                attr = node.attributes[i].nodeName;
                if (attr.indexOf('on') === 0) {
                    syntax = esprima.parse(node.getAttribute(attr));
                    node.setAttribute(attr, checkRingPrivilege(body,
                        elementsId, elementsClass, syntax, ring));
                }
            }
        }
    }

    function processBody(body) {
        // console.log(body);
        var elementsId = {},
            elementsClass = {},
            nodes = [],
            node,
            cls,
            i = 0,
            j;
        nodes.push.apply(nodes, body.children);
        // Add collections for id and class selector
        while (i != nodes.length) {
            node = nodes[i];
            // console.log(i, node, findRing(node));
            if (node.id) {
                elementsId[node.id] = node;
            }
            if (node.classList && node.classList.length) {
                cls = node.classList;
                j = cls.length;
                while (j--) {
                    if (!elementsClass[cls[j]]) {
                        elementsClass[cls[j]] = [];
                    }
                    elementsClass[cls[j]].push(node);
                }
                // test = node;
            }
            nodes.push.apply(nodes, node.children);
            i += 1;
        }
        // Process nodes contain script
        i = nodes.length;
        while (i--) {
            if (hasScript(nodes[i])) {
                processScript(body, elementsId, elementsClass, nodes[i]);
            }
        }
    }

    function writePage(data) {
        var parser,
            html,
            body;
        if (window.DOMParser) {
            parser = new DOMParser();
            html = parser.parseFromString(data, 'text/xml').documentElement;
            // console.log(html);
            body = html.getElementsByTagName('body')[0];
            if (html.firstChild.nodeName !== 'parsererror' &&
                body && body.firstChild.nodeName !== 'parsererror') {
                processBody(body);
                if (document.body == null) {
                    document.write(html.outerHTML);
                }
            } else {
                console.log('WARNING: error parsing the HTML');
                if (document.body == null) {
                    document.write(data);
                }
            }
        }
    }

    $.ajax({
        url: document.URL,
        success: function(data) {
            if (document.body == null) {
                // console.log(data);
                writePage(data);
            }
        }
    });
}());